<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        /* 
        需求，实现vue中method方法的调用，在create生命周期方法中调用实例上面的方法
             并且实现对实例数据的修改和查询，其中涉及到响应式和属性的代理操作
 
              methods: {
                 getName() {
                     return this.name
                 },
                 setName(Nname) {
                     return this.name = Nname
                 }
             },
             created () {
                 let name = this.getName()
                 console.log(name)
                 this.setName('修改')
                 console.log(this.getName())
             },
 
        */



        // 首先是将data属性挂载到vm实例上面
        // 这里简单写一下，主要功能放在实现this绑定上面，
        class Vue {
            constructor(options) {
                this._data = options.data
                // 初始化数据
                this.InitDate()
                // 初始化方法
                this.InitMethod(options.methods)
                // 之后调用create方法执行methods中的方法，测试是否成功
                this.testMethod(options)
            }
            InitMethod(methods) {
                console.log('这是：',this)
                // 将方法和当前的实例进行this的绑定,并且将方法添加到实例上面作为实例的属性
                for(let key in methods){
                    this[key] = methods[key].bind(this)
                }
            }
            InitDate() {
                React(this._data, this)
            }
            testMethod(options){
                // 当然这里绑定this之后，在create方法中调用的方法都是通过this.xxx调用的，
                // 所以被调用的方法的this本身就指向了当前的vue实例，所以上面方法绑定this是否成功无法
                // 明显的看出，所以我们加上一种直接调用methods中的方法
                options.created.call(this)
                // 这样就使得SayHello调用者是window对象，这样就看出了使用的this不是window，而是经过绑定的实例对象
                // 证明上面的方法绑定成功了
                let SayHello = this.SayHello
                console.log(SayHello())
            }
        }
        // 设置属性为响应式,这个函数只能处理常见的数据类型
        let _toString = Object.prototype.toString
        function defineReactive(obj, key, val) {
            // 如果是对象
            if (_toString.call(val) === '[object Object]') {
                for (let key in val) {
                    defineReactive(val, key, val[key])
                }
            }
            // 如果是数组
            else if (_toString.call(val) === '[object Array]') {
                for (let i = 0; i < val.length; i++) {
                    defineReactive(val, i, val[i])
                }
            }
            // 如果是基本数据类型
            else {
                Object.defineProperty(obj, key, {
                    get() {
                        return val
                    },
                    // 这里只考虑基本数据类型
                    set(newVal) {
                        val = newVal
                    }
                })
            }
        }
        function React(obj, vm) {
            for (let key in obj) {
                defineReactive(obj, key, obj[key])
                proxy(vm, '_data', key)
            }
        }
        // 实现属性的代理，将_data中的属性直接挂载到vue实例上面
        function proxy(obj, prop, key) {
            Object.defineProperty(obj, key, {
                get() {
                    return obj[prop][key]
                },
                set(newVal) {
                    obj[prop][key] = newVal
                }
            })
        }



        let vue = new Vue({
            el: '#root',
            data: {
                name: {
                    firstname: '李',
                    lastname: 'hua',
                    list: [{ 'math': { one: 100, two: 99 } }, { 'en': { one: 101, two: 95 } }]
                },
                list: ['f', 'g'],
                message: '你好啊！'
            },
            methods: {
                getMsg() {
                    return this.message
                },
                setMsg(Nname) {
                    return this.message = Nname
                },
                SayHello(){
                    console.log('当前this指向：',this)
                    return "我叫"+this.name.firstname + this.name.lastname+'，你叫什么？'
                }
            },
            created() {
               // console.log(this)
                let name = this.getMsg()
                console.log(name)
                this.setMsg('修改')
                console.log(this.getMsg())
            },
        })
    </script>
</body>

</html>